<template>
  <div style="height: 100%;background: transparent">
    <el-scrollbar height="100%">
      <div class="dev_satate_wrapper">
        <div class="left_alarm_message_wrapper">
          <div class="title_wrapper">
            <div class="left_title">
              预警消息
            </div>
            <div class="right_more">
              更多消息 >>
            </div>
          </div>
          <el-scrollbar class="content_wrapper" >
            <div style="height: 100%;">
              <el-timeline style="max-width: 600px">
                <el-timeline-item v-for="(item,index) in state.dataList" :key="index" :color="index===0 ? '#128b0f' : ''"
                                  :timestamp="item.createTime" placement="top">
                  <div class="message_card_item ">
                    <div :class="'message ' + getMessageClass(item.messageType)">
                      {{ getMessageContent(item.messageType) }}
                    </div>
                    <div class="image">
                      <el-image :src="thisPageState.imgBaseUrl + getImageUrl(item)" fit="cover" @click="previewImg(item,index,true)">
                      </el-image>
                    </div>
                  </div>
                </el-timeline-item>
              </el-timeline>
            </div>
          </el-scrollbar>
        </div>
        <div class="right_dev_run_model_wrapper">
          <div class="title_wrapper">
            <div class="left_title">
              运行状态
            </div>
            <div class="right_more">
            </div>
          </div>
          <div class="model_three_wrapper">
            <div ref="dev_model_container" style="width: 100%;height: 100%;"></div>
          </div>
        </div>
      </div>
    </el-scrollbar>
  </div>

  <!--  预览检测后的图片-->
  <el-image-viewer v-if="thisPageState.showCapImgDetected" :url-list="thisPageState.camCapDetectedList"
                   :initial-index="thisPageState.capImgDetectedViewIndex"
                   @close="thisPageState.showCapImgDetected = false">
  </el-image-viewer>
</template>

<script lang="ts" setup name="devState">

import {onMounted, ref} from "vue";
import {fetchList, delObjs} from "/@/api/iot/iotDeviceMessage";


import * as THREE from 'three';

import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { RoomEnvironment } from 'three/addons/environments/RoomEnvironment.js';
import {BasicTableProps, useTable} from "/@/hooks/table";

let scene, renderer, camera, stats;
let model, skeleton, mixer, clock;

const crossFadeControls = [];

let idleAction, walkAction, runAction;
let idleWeight, walkWeight, runWeight;
let actions, settings;



let singleStepMode = false;
let sizeOfNextStep = 0;
const dev_model_container = ref(null)//document.getElementById( 'container' );


function init() {

  console.log("模型挂在元素：",dev_model_container.value.clientWidth ,dev_model_container.value.clientHeight )
  let element_width=dev_model_container.value.clientWidth
  let element_heiht=dev_model_container.value.clientHeight

  renderer = new THREE.WebGLRenderer( { antialias: true } );
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( element_width, element_heiht );
  renderer.shadowMap.enabled = true; //：启用 阴影映射（shadow map），使场景中的物体可以投射和接收阴影。




  //window.innerWidth / window.innerHeight
  camera = new THREE.PerspectiveCamera( 40,element_width/element_heiht , 1, 1000 );
  camera.position.set( 0.07, 4.5, 20 );
  camera.lookAt( 5, 30, -20 );

  clock = new THREE.Clock();


  const pmremGenerator = new THREE.PMREMGenerator( renderer ); //于生成预过滤的环境贴图（PMREM）。这是为环境贴图提供高质量渲染的一种技术。

  scene = new THREE.Scene();
  // scene.background = new THREE.Color( 0xa0a0a0 );
  scene.background = new THREE.Color( 0xffffff );
  // scene.environment = pmremGenerator.fromScene( new RoomEnvironment(), 0.04 ).texture;
  // scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );


  /**
   * 半球光是一种模拟天空和地面不同光照强度的光源。它有两个主要参数：
   * 第一个参数是 上半球颜色（即天空的颜色），值为 0xffffff，表示白色。
   * 第二个参数是 下半球颜色（即地面或环境的颜色），值为 0x8d8d8d，表示较暗的灰色。
   * 第三个参数是光的 强度，即光源的亮度，这里设置为 3，表示较强的光照。
   * 用途：
   *
   * 半球光通常用于模拟天空和地面的均匀光照效果，常见于户外环境。
   * 它给场景带来了 柔和的全局光照，没有明确的方向性，适合用于填充光，尤其是模拟阳光照射到大气中的散射效果。
   * @type {HemisphereLight}
   */
  const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x8d8d8d, 3 );
  hemiLight.position.set( 0, 20, 0 );
  scene.add( hemiLight );


  /**
   * 创建平行光
   * 第一个参数 0xffffff 是光的颜色，表示 白色。
   * 第二个参数 3 是光的 强度，表示光源的亮度。
   * 用途：
   *
   * 平行光通常模拟太阳光等远距离光源，光线是平行的，且强度均匀。
   * 这种光源具有 明确的方向性，因此它能产生有明确方向和投射阴影的效果。
   * @type {DirectionalLight}
   */
  const dirLight = new THREE.DirectionalLight( 0xffffff, 3.5 );
  dirLight.position.set( - 3, 15, - 10 );
  dirLight.castShadow = true; //启用平行光的 阴影 功能。
//   平行光的阴影效果由 阴影摄像机 来控制，这个摄像机用于定义光源投射阴影的范围和大小。
// dirLight.shadow.camera.top = 2 设置阴影摄像机的 上裁剪面 为 2，这意味着阴影摄像机的视野上边界为 2 单位。
// 用途：
// 控制阴影摄像机的裁剪区域，避免场景中的不必要区域被渲染出来。如果裁剪面设置得过小，阴影可能无法完全显示。 我的理解是设置和camera举例小于2的没有阴影
  dirLight.shadow.camera.top = 2; //设置 平行光阴影摄像机的顶部裁剪面。
  dirLight.shadow.camera.bottom = - 2;
  dirLight.shadow.camera.left = - 2;
  dirLight.shadow.camera.right = 2;
  dirLight.shadow.camera.near = 0.1; //设置 平行光阴影摄像机的近裁剪面。
  dirLight.shadow.camera.far = 100;
  scene.add( dirLight );



  // scene.add( new THREE.CameraHelper( dirLight.shadow.camera ) );

  // ground

  //创建一个 平面网格（mesh），它使用了 PlaneGeometry 几何体和 MeshPhongMaterial 材质。
  //这是为平面创建的 Phong 材质，MeshPhongMaterial 用于模拟带有光泽表面的材质，并根据光照来渲染物体表面的反射光。
  // 参数 color: 0xcbcbcb 表示材质的 颜色，这里设置为一个浅灰色（RGB值 0xcbcbcb），即接近白色的灰色。
// depthWrite: false：设置为 false，表示在渲染时不写入深度缓冲区。这个属性通常用于使物体不影响深度测试或渲染排序。
//  mesh.rotation.x = - Math.PI / 2; 将平面绕 X 轴 旋转了 -π/2 弧度（即 90 度），这意味着平面将从默认的竖直位置旋转到水平位置。
//  启用平面网格的 接收阴影 功能。
  const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 100, 100 ), new THREE.MeshPhongMaterial( { color: 0xffffff, depthWrite: false } ) );
  mesh.rotation.x = - Math.PI / 2;
  mesh.receiveShadow = true;
  scene.add( mesh );




  const loader = new GLTFLoader();
  loader.load( '/models/gltf/grenhouseEdit.glb', function ( gltf ) {

    model = gltf.scene;
    model.position.set( 0, 0, 0 );
    model.scale.set( 0.65, 0.65, 0.65 );
    scene.add( model );


    /**
     *          //model.traverse() 是一个递归方法，它遍历模型的所有子对象。
     * //          在回调函数中，object.isMesh 用于检查当前对象是否是 网格（Mesh）。如果是网格，就设置 object.castShadow = true，使该网格能够 投射阴影。
     * // castShadow 是 Three.js 中的属性，用于设置物体是否投射阴影。只有启用了 castShadow = true 的物体才会投射阴影。
     * // 用途：
     * //
     * // 为所有网格对象启用阴影投射，常用于角色、物体等可以与光源互动并产生阴影的模型。
     *
     * (简单理解模型就是网格对象）在 Three.js 中，网格对象（Mesh）是构成三维模型的基础单元之一。网格对象是由 几何体（Geometry 或 BufferGeometry）和 材质（Material）结合而成的。可以理解为一个可渲染的物体，它在场景中占据空间，并且可以显示、旋转、缩放等。其他类型的对象，比如 灯光（Light）、相机（Camera）等，与网格对象的功能和作用有所不同。
     */
    model.traverse( function ( object ) {

      if ( object.isMesh ) object.castShadow = true;

    } );

    /**
     * skeleton = new THREE.SkeletonHelper( model );
     * 作用：创建一个 骨架辅助对象（SkeletonHelper），用于显示骨架结构。
     *
     * 详细解释：
     *
     * new THREE.SkeletonHelper(model) 创建一个 骨架助手，它可视化模型的骨架。model 是加载的 3D 模型，SkeletonHelper 会读取模型中的骨骼信息，并以线条的形式显示出来，帮助开发者查看和调试骨骼动画。
     * 用途：
     *
     * 该工具通常用于调试动画和骨骼系统，帮助开发者理解和修改骨骼结构。比如，模型的 角色动画（如走路、跳跃等）依赖于骨骼系统来控制姿势和运动。
     */

    skeleton = new THREE.SkeletonHelper( model );
    skeleton.visible = false; //将骨架助手的 可见性 设置为 false，使其在场景中不可见。
    scene.add( skeleton );

    //

    // createPanel();


    //

    const animations = gltf.animations;

    mixer = new THREE.AnimationMixer( model );

    actions=[]
    animations.map( function ( animation ) {

      const action=mixer.clipAction( animation );
      actions.push(action)
    } );

    activateAllActions();

    renderer.setAnimationLoop( animate );

  } );


  dev_model_container.value.appendChild( renderer.domElement );

  // stats = new Stats();
  // dev_model_container.value.appendChild( stats.dom );

  //OrbitControls：启用相机的轨道控制，可以用鼠标旋转、缩放和移动视角。
// controls.target.set：设置控制器的目标，即相机始终对准的点。 通常就是模型的位置即原点
// controls.enablePan：禁用平移操作，只允许旋转和缩放。
// controls.enableDamping：启用阻尼效果，使得相机运动更加平滑。
  const controls = new OrbitControls( camera, renderer.domElement );
  controls.target.set( 0, 0.5, 0 );
  controls.update();
  controls.enablePan = false;
  controls.enableDamping = true;


  window.addEventListener( 'resize', onWindowResize );

}





function deactivateAllActions() {

  /**
   * 停止所有动画
   */
  actions.forEach( function ( action ) {

    action.stop();

  } );

}

function activateAllActions() {

  /**
   * 设置某个动画在所有动画中的权重，即优先播放哪一个动画，权重为0表示这个动画不播放 为1表示播放
   */
  // setWeight( idleAction, settings[ 'modify idle weight' ] );
  // setWeight( walkAction, settings[ 'modify walk weight' ] );
  // setWeight( runAction, settings[ 'modify run weight' ] );

  /**
   * 所有动画都播放
   */
  actions.forEach( function ( action ) {

    action.play();

  } );

}










function onWindowResize() {
  console.log("模型挂在元素：",dev_model_container.value.clientWidth ,dev_model_container.value.clientHeight )
  let element_width=dev_model_container.value.clientWidth
  let element_heiht=dev_model_container.value.clientHeight


  camera.aspect = element_width/element_heiht; //window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize( element_width, element_heiht );

}

function animate() {

  // idleWeight = idleAction.getEffectiveWeight();
  // walkWeight = walkAction.getEffectiveWeight();
  // runWeight = runAction.getEffectiveWeight();

  // Update the panel values if weights are modified from "outside" (by crossfadings)

  // updateWeightSliders();

  // Enable/disable crossfade controls according to current weight values

  // updateCrossFadeControls();

  // Get the time elapsed since the last frame, used for mixer update (if not in single step mode)

  let mixerUpdateDelta = clock.getDelta();

  // If in single step mode, make one step and then do nothing (until the user clicks again)

  if ( singleStepMode ) {

    mixerUpdateDelta = sizeOfNextStep;
    sizeOfNextStep = 0;

  }

  // Update the animation mixer, the stats panel, and render this frame

  mixer.update( mixerUpdateDelta );

  renderer.render( scene, camera );

  // stats.update();

}


/**
 * 定义组件属性 接收传递来的 大棚ID
 */
const props = defineProps({
  iotDevice: {
    type:Object,
    default: () => {},
  },
})

const state: BasicTableProps = reactive<BasicTableProps>({
  queryForm: {
    iotDeviceId: props.iotDevice.id,
  },
  pageList: fetchList,
  handleDataListcallBack: getPreviewImagListByDataTable,
  dataList: [] // 初始化 dataList
})

//  table hook
const {
  getDataList,
  currentChangeHandle,
  sizeChangeHandle,
  sortChangeHandle,
  downBlobFile,
  tableStyle
} = useTable(state)


/**
 * 自己定义的页面state
 */
const thisPageState=reactive({

  camCapDetectedList:[],
  showCapImgDetected:false,
  capImgDetectedViewIndex:0,
  imgBaseUrl:"/api"
})
/**
 * 获取后端地址
 */
thisPageState.imgBaseUrl=import.meta.env.VITE_API_URL

// 获取图片 URL 的函数
const getImageUrl = (item) => {
  const match = item.messageSpecialInfo.match(/url=([^}]+)/);
  return match ? match[1] : '';
};
// 根据 messageType 获取消息类名的函数
const getMessageClass = (messageType) => {
  if (messageType === 'BINGCHONG_ALARM') {
    return 'error_image';
  } else if (messageType === 'BEST_ALARM') {
    return 'best';
  }
  return '';
};

// 根据 messageType 获取消息内容的函数
const getMessageContent = (messageType) => {
  if (messageType === 'BINGCHONG_ALARM') {
    return '注意：经系统检测，此区域部分多肉可能出现病虫害，请及时处理！';
  } else if (messageType === 'BEST_ALARM') {
    return '经系统识别，此区域部分多肉植株生长很好，极大可能适用于制作生长繁育的叶片。';
  }
  return '健康';
};
/**
 * 获取数据图片的预览列表
 */
function getPreviewImagListByDataTable(){
  state.dataList = state.dataList.sort((a, b) => {
    const timeA = new Date(a.createTime).getTime();
    const timeB = new Date(b.createTime).getTime();
    return timeB - timeA;
  });

  const list = state.dataList.map(item => thisPageState.imgBaseUrl + getImageUrl(item));
  thisPageState.camCapDetectedList = list;
};

/**
 * 预览图片
 * @param scope
 * @param isDetected
 */
function previewImg(scope,index,isDetected){
  if(isDetected){
    thisPageState.showCapImgDetected=true
    thisPageState.capImgDetectedViewIndex=index
  }
}

onMounted(()=>{
  //初始化模型
  init();
  getDataList();
})



</script>

<style scoped lang="scss">
.dev_satate_wrapper{
  height: 530px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 18px;
  .left_alarm_message_wrapper{
    flex: 3;
    height: 100%;
    //background: #4ab7bd;
    padding: 15px 15px;
    //border:1px #dcdfe6 solid;
    overflow: hidden;
    background: white;
    .title_wrapper{
      height: 20px;
      display: flex;
      width: 100%;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
      .left_title{
        font-size: 16px;
        //font-weight: bold;
      }
      .right_more{
        font-size: 12px;
        cursor: pointer;
        color: var(--el-color-primary);
      }
    }
    .content_wrapper{
      height: 465px;
      //text-align: center;
      padding: 15px 20px;
      //overflow: auto;
      .message_card_item{
        background: #f1f1f1;
        padding: 15px;

        .message.error_image{
          color: red;//var(--el-color-error);
        }
        .message.best{
          color: var(--el-color-primary);
        }
        .image{
          margin-top: 10px;
          width: 100%;
          height: 13vw;
          text-align: center;
          overflow: hidden;
          .el-image{
            width: 100%;
            height: 100%;
          }
        }
      }
    }

  }
  .right_dev_run_model_wrapper{
    flex: 6.5;
    height: 100%;
    padding: 15px 15px;
    overflow: hidden;
    background: white;
    .title_wrapper{
      height: 20px;
      display: flex;
      width: 100%;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 15px;
      .left_title{
        font-size: 16px;
        //font-weight: bold;
      }
      .right_more{
        color: var(--el-color-primary);
      }
    }
  }
  .model_three_wrapper{
    height: 465px;
    overflow: hidden;
  }
}
</style>